"""

Copyright MUAMP 2021 MUAMP.COM David French

This Circuit Python code to control a Robot Quadruped "PICO Python Dog" can be freely used and modified,

except for commercial and provided this 6 line comment remains in the code at the top of the .py file

and these 6 lines are not modified or removed.

"""


import time

import board

import busio

import pwmio

from adafruit_motor import servo

import adafruit_icm20x

from math import asin, acos, atan, degrees, sqrt

import pulseio

import adafruit_irremote


# IR sensor

IR_PIN = board.GP9 # Pin GP9 connected to IR receiver

# IR decoder

pulsein = pulseio.PulseIn(IR_PIN, maxlen=100, idle_state=True)

decoder = adafruit_irremote.GenericDecode()

pulsein.clear()

pulsein.resume()


# Acc Gyro Mag test setup on i2c (1)

i2c = busio.I2C(board.GP11, board.GP10)

icm = adafruit_icm20x.ICM20948(i2c)


# AGM functions

def Acc(): # Acc +/- 10 for 90 degrees of x and y axis (wobble board +...)

Ax = icm.acceleration[0] # Gyro detects sudden movement in real time (compliance +...)

Ay = icm.acceleration[1] # Mag (compass) for up side down detection

return Ax, Ay


# Servo setup (Raspberry Pi PICO micro controller (Circuit Python) with Tower Pro MG90S servos

FL_knee = servo.Servo(pwmio.PWMOut(board.GP0, duty_cycle=0, frequency=50), min_pulse=500, max_pulse=2400)

FL_hip = servo.Servo(pwmio.PWMOut(board.GP1, duty_cycle=0, frequency=50), min_pulse=500, max_pulse=2400)

FR_knee = servo.Servo(pwmio.PWMOut(board.GP27, duty_cycle=0, frequency=50), min_pulse=500, max_pulse=2400)

FR_hip = servo.Servo(pwmio.PWMOut(board.GP26, duty_cycle=0, frequency=50), min_pulse=500, max_pulse=2400)

BL_knee = servo.Servo(pwmio.PWMOut(board.GP14, duty_cycle=0, frequency=50), min_pulse=500, max_pulse=2400)

BL_hip = servo.Servo(pwmio.PWMOut(board.GP15, duty_cycle=0, frequency=50), min_pulse=500, max_pulse=2400)

BR_knee = servo.Servo(pwmio.PWMOut(board.GP21, duty_cycle=0, frequency=50), min_pulse=500, max_pulse=2400)

BR_hip = servo.Servo(pwmio.PWMOut(board.GP20, duty_cycle=0, frequency=50), min_pulse=500, max_pulse=2400)


# servo function - converts height, slew, elevation and roll inputs to degrees of angles of servos

def servo_drive(height, slew, elev, roll):

if height > 95: height = 95

if height < 5: height = 5

if slew < -33: slew = -33

if slew > 33: slew = 33

if elev < -20: elev = -20

if elev > 20: elev = 20

if roll < -20: roll = -15

if roll > 20: roll = 15

if elev != 0:

F_height = height - elev

B_height = height + elev

hip_offset = asin(elev / 49)

else:

F_height = height

B_height = height

hip_offset = 0


F_height_slew = sqrt((F_height * F_height) + (slew * slew))

B_height_slew = sqrt((B_height * B_height) + (slew * slew))


if roll != 0:

F_L_height = F_height_slew - roll

F_R_height = F_height_slew + roll

B_L_height = B_height_slew - roll

B_R_height = B_height_slew + roll

else:

F_L_height = F_height_slew

F_R_height = F_height_slew

B_L_height = B_height_slew

B_R_height = B_height_slew


F_L_knee = acos(1 - ((F_L_height * F_L_height) / 5000))

F_R_knee = acos(1 - ((F_R_height * F_R_height) / 5000))

B_L_knee = acos(1 - ((B_L_height * B_L_height) / 5000))

B_R_knee = acos(1 - ((B_R_height * B_R_height) / 5000))


F_L_hip = (F_L_knee / 2) + atan(slew / F_L_height) + hip_offset

F_R_hip = (F_R_knee / 2) + atan(slew / F_R_height) + hip_offset

B_L_hip = (B_L_knee / 2) + atan(slew / B_L_height) + hip_offset

B_R_hip = (B_R_knee / 2) + atan(slew / B_R_height) + hip_offset


F_L_hip = degrees(F_L_hip)

F_R_hip = degrees(F_R_hip)

F_L_knee = degrees(F_L_knee)

F_R_knee = degrees(F_R_knee)

B_L_hip = degrees(B_L_hip)

B_R_hip = degrees(B_R_hip)

B_L_knee = degrees(B_L_knee)

B_R_knee = degrees(B_R_knee)


if F_L_hip > 175: F_L_hip = 175

if F_R_hip > 175: F_R_hip = 175

if B_L_hip > 175: B_L_hip = 175

if B_R_hip > 175: B_R_hip = 175

if F_L_hip < 5: F_L_hip = 5

if F_R_hip < 5: F_R_hip = 5

if B_L_hip < 5: B_L_hip = 5

if B_R_hip < 5: B_R_hip = 5

if F_L_knee > 175: F_L_knee = 175

if F_R_knee > 175: F_R_knee = 175

if B_L_knee > 175: B_L_knee = 175

if B_R_knee > 175: B_R_knee = 175

if F_L_knee < 5: F_L_knee = 5

if F_R_knee < 5: F_R_knee = 5

if B_L_knee < 5: B_L_knee = 5

if B_R_knee < 5: B_R_knee = 5

FL_knee.angle = 180 - F_L_knee

FL_hip.angle = 180 - F_L_hip

FR_knee.angle = F_R_knee

FR_hip.angle = F_R_hip

BL_knee.angle = 180 - B_L_knee

BL_hip.angle = 180 - B_L_hip

BR_knee.angle = B_R_knee

BR_hip.angle = B_R_hip



""" MAIN CODE """


time_delay = 0.3

scale = 1


h = 0 # starting laying down

s = 0 # starting with feet direcly under hips

e = 0 # starting with body level on elevation

r = 0 # starting with body level on roll



time.sleep(time_delay *2) # start delay



for h_bar in range(0, 650, scale): # start position from lay

h=h_bar/10

servo_drive(h, s, e, r)

time.sleep(time_delay / 100)

time.sleep(time_delay *1)



h = 65

while True:

Ax, Ay = Acc()

if Ax > 5: e += 4 # Needs fine adjustment with fractions

elif Ax > 4: e += 3

elif Ax > 3: e += 2

elif Ax > 2: e += 1

elif Ax > 1: e += 0.5

elif Ax < -5: e -= 4

elif Ax < -4: e -= 3

elif Ax < -3: e -= 2

elif Ax < -2: e -= 1

elif Ax < -1: e -= 0.5


if Ay > 2: r += 1

elif Ay > 1: r += 0.5

elif Ay <-2: r -= 1

elif Ay <-1: r -= 0.5


if h > 95: h = 95

if h < 5: h = 5

if s < -33: s = -33

if s > 33: s = 33

if e < -20: e = -20

if e > 20: e = 20

if r < -20: r = -15

if r > 20: r = 15


servo_drive(h, s, e, r)